package xyz.jiang.gateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import xyz.jiang.common.grace.result.GraceJSONResult;
import xyz.jiang.common.grace.result.ResponseStatusEnum;
import xyz.jiang.common.utils.IPUtil;
import xyz.jiang.common.utils.RedisUtils;

import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@RefreshScope
public class IPLimitFilter implements GlobalFilter, Ordered {

    private final RedisUtils redisUtils;

    @Value("${blackIp.continueCounts}")
    private Integer continueCounts;

    @Value("${blackIp.timeInterval}")
    private Integer timeInterval;

    @Value("${blackIp.limitTimes}")
    private Integer limitTimes;

    public IPLimitFilter(RedisUtils redisUtils) {
        this.redisUtils = redisUtils;
    }

    @Override
    public int getOrder() {
        return 1;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info("continueCounts: {}", continueCounts);
        log.info("timeInterval: {}", timeInterval);
        log.info("limitTimes: {}", limitTimes);

        return doLimit(exchange, chain);
    }
    // 判断某个请求的ip在2秒内是否请求超过3次，如果超过3次，则限制访问30秒

    /**
     * 限制ip请求次数的判断
     * @param exchange
     * @param chain
     * @return
     */
    public Mono<Void> doLimit(ServerWebExchange exchange,
                              GatewayFilterChain chain) {

        // 根据request获得请求ip
        ServerHttpRequest request = exchange.getRequest();
        String ip = IPUtil.getIP(request);

        // 正常的ip定义
        final String ipRedisKey = "gateway-ip:" + ip;
        // 被拦截的黑名单ip，如果在redis中存在，则表示目前被关小黑屋
        final String ipRedisLimitKey = "gateway-ip:limit:" + ip;

        // 获得当前的ip并且查询还剩下多少时间，如果时间存在（大于0），则表示当前仍然处在黑名单中
        long limitLeftTimes = redisUtils.getExpire(ipRedisLimitKey);
        if (limitLeftTimes > 0) {
            // 终止请求，返回错误
            return GraceJSONResult.renderErrorMsg(exchange, ResponseStatusEnum.SYSTEM_ERROR_BLACK_IP);
        }

        // 在redis中获得ip的累加次数
        long requestCounts = redisUtils.increment(ipRedisKey, 1);
        /**
         * 判断如果是第一次进来，也就是从0开始计数，则初期访问就是1，
         * 需要设置间隔的时间，也就是连续请求的次数的间隔时间
         */
        if (requestCounts == 1) {
            redisUtils.setExpireSeconds(ipRedisKey, timeInterval);
        }
        if (requestCounts > continueCounts) {
            // 限制ip访问的时间[limitTimes]
            redisUtils.setEx(ipRedisLimitKey, ip, limitTimes, TimeUnit.SECONDS);
            // 终止请求，返回错误
            return GraceJSONResult.renderErrorMsg(exchange, ResponseStatusEnum.SYSTEM_ERROR_BLACK_IP);
        }
        return chain.filter(exchange);
    }

}
